use std::iter::zip;

#[derive(PartialEq, Debug)]
pub struct Data {
    time: u64,
    distance: u64,
}

impl Data {
    pub fn new(time: u64, distance: u64) -> Self {
        Self {
           time,
           distance 
        }
    }

    pub fn beat_record_size(&self) -> u64 {
        for hold_ms in 0..(self.time / 2) {
            let speed: u64 = hold_ms;
            let boat_time: u64 = self.time - hold_ms;
            let real_distance: u64 = speed * boat_time;
            if real_distance >= self.distance {
                return self.time + 1 - 2 * hold_ms;
            }
        }
        0u64
    }
}

pub fn get_part1_datas(time_str: &str, distance_str: &str) -> Vec<Data> {
    let time_str_colon_pos = time_str.find(":").unwrap();
    let distance_str_colon_pos = distance_str.find(":").unwrap();
    let time_str = time_str[(time_str_colon_pos+1)..].trim();
    let distance_str = distance_str[(distance_str_colon_pos+1)..].trim();

    let get_numbers = |s: &str| -> Vec<u64> {
        s.split(" ")
         .filter(|t| !t.trim().is_empty())
         .map(|t| t.trim().parse().unwrap())
         .collect()
    };

    let times = get_numbers(time_str);
    let distances = get_numbers(distance_str);
    zip(times, distances).map(|(t, d)| Data::new(t, d)).collect()
}

pub fn get_part2_data(time_str: &str, distance_str: &str) -> Data {
    let time_str_colon_pos = time_str.find(":").unwrap();
    let distance_str_colon_pos = distance_str.find(":").unwrap();
    let time_str = time_str[(time_str_colon_pos+1)..].trim();
    let distance_str = distance_str[(distance_str_colon_pos+1)..].trim();

    let get_number = |s: &str| -> u64 {
        let res: String = s.chars().filter(|c| c.is_digit(10)).collect();
        res.parse().unwrap()
    };

    let time = get_number(time_str);
    let distance = get_number(distance_str);

    Data::new(time, distance)
}

#[cfg(test)]
mod test {
    use super::*;

    #[test]
    fn test_create_part1_datas() {
        let datas = get_part1_datas("Time:      7  15   30", "Distance:  9  40  200");
        assert_eq!(
            datas,
            vec![Data::new(7, 9), Data::new(15, 40), Data::new(30, 200)]
        );
    }

    #[test]
    fn test_create_part2_data() {
        let data = get_part2_data(
            "Time:        38     94     79     70", 
            "Distance:   241   1549   1074   1091"
        );
        assert_eq!(
            data,
            Data::new(38947970, 241154910741091)
        );
    }
}